package channel

import (
	"fmt"
	"math/rand"
	"os"
	"os/signal"
	"runtime"
	"sync"
	"time"
)

// 控制并发数量
func ChannelGoroutineNumCtl() {
	// 1 独立的goroutine输出goroutine数量
	go func() {
		for {
			fmt.Println("goroutine num: ", runtime.NumGoroutine())
			time.Sleep(500 * time.Millisecond)
		}
	}()
	// 2 初始化channel，设置缓冲大小（并发规模）
	const size = 1024
	ch := make(chan struct{}, size)

	// 3 并发的goroutine
	for {
		ch <- struct{}{}
		go func() {
			time.Sleep(10 * time.Second)
			<-ch
		}()
	}
}

// 信号等待
func SelectSignal() {
	go func() {
		for true {
			fmt.Println(time.Now().Format(".15.04.05.000000"))
			time.Sleep(500 * time.Millisecond)
		}
	}()

	// 主goroutine等待其他goroutine的三种方式
	// 1.time.Sleep
	// 2.WaitGroup
	// 3.select{}  // 最好用这个

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, os.Interrupt, os.Kill)

	select {
	case <-ch:
		fmt.Println("received os signed: Interrupt or Kill")
	}
}

// select 选择关闭 goroutine
func SelectChannelClose() {
	// 定义无缓冲channel
	wg := sync.WaitGroup{}
	ch := make(chan struct{})

	// goroutine，用来close，表示发出信号
	wg.Add(1)
	go func() {
		defer wg.Done()
		time.Sleep(3 * time.Second)
		fmt.Println("input signal and close channel")
		close(ch)
	}()

	// goroutine，用来接收信号
	wg.Add(1)
	go func() {
		defer wg.Done()
		for {
			select {
			case <-ch:
				fmt.Println("Signal received")
				return
			default:
			}

			fmt.Println("Wait for the signal to be received...")
			time.Sleep(time.Millisecond * 300)
		}
	}()

	wg.Wait()
}

// Race模式经典案例
func SelectChannelRace() {
	// 1.初始化数据
	// 模拟查询结果，需要与具体的querier取得联系
	type Rows struct {
		// 数据字段
		// 。。。。。。
		// 索引标识
		Index int
	}
	// 模拟的querier数量
	const QuerierNum = 8
	// 用于通信的channel 1.数据channel 2.停止信号channel
	ch := make(chan Rows, 1)
	stopsChs := [QuerierNum]chan struct{}{}
	for i := 0; i < QuerierNum; i++ {
		stopsChs[i] = make(chan struct{})
	}
	// wg,rand
	wg := sync.WaitGroup{}
	rand.Seed(time.Now().UnixMilli())

	// 2.模拟querier查询，每个查询持续时间不同
	wg.Add(QuerierNum)
	for i := 0; i < QuerierNum; i++ {
		// 每一个querier
		go func(i int) {
			defer wg.Done()
			// 模拟查询时间
			randD := rand.Intn(1000)
			fmt.Println("querier ", i, " start fetch data, need duration is ", randD, " ms.")

			// 查询结果的channel
			chRst := make(chan Rows, 1)

			// 执行查询工作
			go func() {
				// 模拟时长
				time.Sleep(time.Duration(randD) * time.Millisecond)
				chRst <- Rows{
					Index: i,
				}
			}()

			// 监听查询结果和停止channel信号
			select {
			case rows := <-chRst:
				fmt.Println("querier ", i, "get result.")
				if len(ch) == 0 {
					ch <- rows
				}
			case <-stopsChs[i]:
				fmt.Println("querier ", i, " is stopping.")
				return
			}
		}(i)
	}

	// 3.等待第一个查询结果的反馈
	wg.Add(1)
	go func() {
		defer wg.Done()
		// 等待ch中传递的结果
		select {
		// 等待第一个查询结果
		case rows := <-ch:
			fmt.Println("get first result from ", rows.Index, ". stop other querier")
			// 循环结构，通知其他全部querier结束
			for i := 0; i < QuerierNum; i++ {
				if rows.Index != i {
					stopsChs[i] <- struct{}{}
				}
			}
		// 设置一个超时时间
		case <-time.After(5 * time.Second):
			fmt.Println("all querier timeout.")
			for i := 0; i < QuerierNum; i++ {
				stopsChs[i] <- struct{}{}
			}
		}
	}()

	wg.Wait()
}

// ALL模式经典案例
func SelectChannelAll() {
	// 1.初始化资源
	type Content struct {
		Subject string
		Tags    []string
		Views   int
		// 表示操作了资源的那一部分
		part string
	}
	// 三个用于表示不同部分的常量
	const (
		PartSubject = "subject"
		PartTags    = "tags"
		PartViews   = "views"
	)
	stopChs := map[string]chan struct{}{
		PartSubject: make(chan struct{}),
		PartTags:    make(chan struct{}),
		PartViews:   make(chan struct{}),
	}
	// 接收和发送操作的通信channel
	ch := make(chan Content, len(stopChs))
	// timeover 设置超时时间
	to := time.After(time.Millisecond * 500)
	wg := sync.WaitGroup{}
	rand.Seed(time.Now().UnixMilli())

	// 2.goroutine执行每个部分的获取
	// 为每个部分开启一个goroutine
	for part := range stopChs {
		wg.Add(1)
		go func(part string) {
			defer wg.Done()
			// 1.初始化信息
			randD := rand.Intn(1000)
			fmt.Println("start fetch ", part, "data, need duration is ", randD, " ms.")

			// 查询结果的channel
			chRst := make(chan Content, 1)

			// 2.模拟延时的获取资源
			go func() {
				// 模拟执行时间
				time.Sleep(time.Duration(randD) * time.Millisecond)
				// 模拟业务逻辑
				content := Content{
					part: part,
				}

				// 基于不同的part，完成不同的属性设置
				switch part {
				case PartSubject:
					content.Subject = "subject of content"
				case PartTags:
					content.Tags = []string{"go", "Goroutine", "Channel", "select"}
				case PartViews:
					content.Views = 1024
				}

				// 发送到chRst
				chRst <- content
			}()

			// 3.监控资源获取成功还是超时到达
			select {
			// 查询到结果
			case rst := <-chRst:
				fmt.Println("querier ", part, " get result.")
				ch <- rst
			// 超时了
			case <-stopChs[part]:
				fmt.Println("querier ", part, " is stopping.")
				return
			}
		}(part)
	}

	// 3.接收每个部分，整合在一起
	wg.Add(1)
	go func() {
		defer wg.Done()
		// 1.初始化资源
		// 整体的资源
		content := Content{}
		// 标识那个部分已经接收完毕
		received := map[string]struct{}{}

		// 2.等待接收或者超时到期
		// 超时时间到要通知未完成的goroutine结束
		// 未到超时时间，将结果整合到一起，并判断是否需要继续等待
	loopReceived:
		for {
			select {
			// 超时
			case <-to:
				fmt.Println("querier timeout. Content is incomplete.")
				// 超时时间到要通知未完成的goroutine结束
				// 遍历stopCh，判断是否存在于received中即可
				for part := range stopChs {
					if _, exist := received[part]; !exist {
						// 不存在说明没处理完，应该关闭
						stopChs[part] <- struct{}{}
					}
				}
				// 关闭
				close(ch)
				// 不在继续监听
				break loopReceived
			// 有处理完的业务
			case rst := <-ch:
				fmt.Println("received some part ", rst.part)
				// 根据不同的part，更新整体content的字段
				switch rst.part {
				case PartSubject:
					content.Subject = rst.Subject
					received[PartSubject] = struct{}{}
				case PartTags:
					content.Tags = rst.Tags
					received[PartTags] = struct{}{}
				case PartViews:
					content.Views = rst.Views
					received[PartViews] = struct{}{}
				}
				// 判断是否已经接收完毕，需要继续等待
				// 先假设接收完毕了
				finish := true
				for part := range stopChs {
					if _, exists := received[part]; !exists {
						// 不存在，说明还有未完成的，没有结束
						finish = false
						break
					}
				}

				// 判定finish
				if finish {
					// 说明全部结束
					fmt.Println("all querier finished. Content is complete")
					close(ch)
					break loopReceived
				}
			}
		}

		fmt.Println(content)
	}()

	wg.Wait()
}

func ChannelSync() {
	wg := sync.WaitGroup{}
	ch := make(chan int)

	wg.Add(2)

	go func() {
		defer wg.Done()
		for i := 1; i < 5; i++ {
			ch <- i
			fmt.Println("Send ", i, ".\tNow:", time.Now().Format("15:04:05.999999999"))
			time.Sleep(1 * time.Second)
		}

		close(ch)
	}()

	go func() {
		defer wg.Done()
		for v := range ch {
			fmt.Println("Received ", v, ".\tNow:", time.Now().Format("15:04:05.999999999"))
			time.Sleep(3 * time.Second)
		}
	}()

	wg.Wait()
}

func ChannelAsync() {
	wg := sync.WaitGroup{}
	ch := make(chan int, 5)

	wg.Add(2)

	go func() {
		defer wg.Done()
		for i := 1; i < 5; i++ {
			ch <- i
			fmt.Println("Send ", i, ".\tNow:", time.Now().Format("15:04:05.999999999"), " len:", len(ch), " cap:", cap(ch))
			time.Sleep(1 * time.Second)
		}

		close(ch)
	}()

	go func() {
		defer wg.Done()
		for v := range ch {
			fmt.Println("Received ", v, ".\tNow:",
				time.Now().Format("15:04:05.999999999"), " len:", len(ch), " cap:", cap(ch))
			time.Sleep(3 * time.Second)
		}
	}()

	wg.Wait()
}

func ChannelGuessNumGame1() {
	// 初始化
	max := 20     // 设置猜数字答案的范围
	counter := 10 // 猜数字的人数
	ch := make(chan int, counter)
	idCh := make(chan int, counter)
	answer := rand.Intn(max)
	fmt.Println("answer for guess num game is: ", answer)
	fmt.Println("---------------------------------------")

	wg := sync.WaitGroup{}

	// 核心代码
	wg.Add(counter)
	for i := 1; i <= counter; i++ {
		go func(ii int) {
			defer wg.Done()
			result := rand.Intn(max)

			fmt.Println("Player No.", ii, " guess ", result)
			if result == answer {
				ch <- result
				idCh <- ii
			}
		}(i)
	}

	wg.Wait()
	fmt.Println("---------------------------------------")

	// 判断是否猜对
	select {
	case res := <-ch:
		fmt.Println("Player No.", <-idCh, " Guess the success, result is ", res)
	default:
		fmt.Println("no player guess the answer... game over... ")
	}
}

func ChannelGuessNumGame2() {
	// 初始化
	max := 20     // 设置猜数字答案的范围
	counter := 10 // 猜数字的人数
	ch := make(chan int)
	//idCh := make(chan int, 1)
	answer := rand.Intn(max)
	fmt.Println("answer for guess num game is: ", answer)
	fmt.Println("---------------------------------------")

	wg := sync.WaitGroup{}

	// 核心代码
	wg.Add(counter)
	counTmp := counter
	for i := 1; i <= counter; i++ {
		go func(ii int) {
			defer wg.Done()
			result := rand.Intn(max)

			fmt.Println("Player No.", ii, " guess ", result)
			if result == answer {
				ch <- result
			}
		}(i)
	}

	wg.Add(1)

	go func() {
		defer wg.Done()
		//counTmp := counter
		//flag := true

		for {
			//if counTmp == 0 {
			//	if flag == true {
			//		time.Sleep(time.Second * 1)
			//		fmt.Println("no player guess the answer, game over...")
			//		return
			//	} else {
			//		time.Sleep(time.Second * 1)
			//		fmt.Println("someone guess the answer, game over...")
			//		return
			//	}
			//}
			if counTmp == 0 {
				time.Sleep(1 * time.Second)
				return
			}

			// 判断是否猜对
			select {
			case res := <-ch:
				fmt.Println("Someone guess success, result is ", res)
				//counTmp--
				//flag = false
				counTmp--
			default:
				//counTmp--
				fmt.Println("Someone guess fail")
				counTmp--
			}
		}
	}()

	wg.Wait()
	fmt.Println("---------------------------------------")
}
